package com.xuecheng.content.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.xuecheng.api.content.model.dto.TeachplanDTO;

import com.xuecheng.api.content.model.vo.TeachPlanVO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.enums.common.CommonEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.TeachPlanEnum;

import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.StringUtil;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.convert.TeachPlanConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.Teachplan;
import com.xuecheng.content.entity.ext.TeachplanNode;
import com.xuecheng.content.mapper.TeachplanMapper;
import com.xuecheng.content.service.CourseBaseService;
import com.xuecheng.content.service.TeachPlanService;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.SocketUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

/*
 * 业务分析：
 *  1.判断关键数据：操作时的必要数据（一般是由前端来传递）
 *       courseId  companyId
 *  2.判断业务数据：操作时，判断数据的状态（一般是判断数据值，数据值来源于后端的数据源）
 *     课程基本信息
 *           判断是否是同一家教学机构
 *           判断课程是否删除
 *  3.获得课程下的所有课程计划数据
 *  4.通过java的递归来生成课程计划树形结构
 *  5.将node数据转为dto并返回
 *
 * */
@Service
public class TeachPlanServiceImpl extends ServiceImpl<TeachplanMapper,Teachplan> implements TeachPlanService {

    @Autowired
    private CourseBaseService courseBaseService;

    @Override
    public TeachplanDTO queryTeachPlanByCourseId(Long courseId, Long companyId) {
        //1.判断关键数据：操作时的必要数据（一般是由前端来传递）
//        if (ObjectUtils.isEmpty(courseId)||
//            ObjectUtils.isEmpty(companyId)){
//            ExceptionCast.cast(CommonErrorCode.E_100101);
//        }
        //2.判断业务数据：操作时，判断数据的状态（一般是判断数据值，数据值来源于后端的数据源）
        // *     课程基本信息
        // *           判断是否是同一家教学机构
        // *           判断课程是否删除
         courseBaseService.getById(courseId, companyId);
        //3.获得课程下的所有课程计划数据
        TeachplanMapper baseMapper = this.getBaseMapper();
        List<TeachplanNode> nodes = baseMapper.selectTeachPlanByCourseId(courseId);

        TeachplanDTO dto = null;

        //4.通过java的递归来生成课程计划树形结构
        if (!(CollectionUtils.isEmpty(nodes))) {
            TeachplanNode rootNode = nodes.remove(0);
            generateTeachNodes(nodes, rootNode);
            //5.将node数据转为dto并返回
            dto = TeachPlanConvert.INSTANCE.node2dto(rootNode);

            return dto;
        }else {
            return dto;
        }

    }

    @Override
    public TeachplanDTO createOrModifyTeachPlan(TeachplanDTO dto, Long companyId) {
        /*
         * 业务分析：（总体步骤）
         *   1.判断业务操作
         *       1.1 是否是添加：dto无id值
         *           添加操作（二级和三级课程计划），并返回TeachplanDTO数据
         *       1.2 是否是修改：dto有id值
         *           修改操作（二级和三级课程计划），并返回TeachplanDTO数据
         *   2.返回结果数据dto
         * */
        System.out.println(dto.getPname());
        Long dtoTeachPlanid = dto.getTeachPlanid();
        if (ObjectUtils.isEmpty(dtoTeachPlanid)){
             TeachplanDTO createDTO = createTeachPlan(dto,companyId);
             return createDTO;
        }else {
            TeachplanDTO modifyDTO = modifyTeachPlan(dto,companyId);
                return modifyDTO;
        }
    }

    @Override
    public void deleteTeachPlan(Long teachPlanId,Long companyId) {
        /*
        业务分析 :
            1.判断关键数据
                teachPlanId
            2.判断业务数据
                课程基本信息
                    判断课程基本信息是否是同一家机构
            3.删除并判断返回值
         */
        if (ObjectUtils.isEmpty(teachPlanId)){
            ExceptionCast.cast(ContentErrorCode.E_120416);
        }
       //select * from teachplan where teachPlanId = #{teachPlanId}
        LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Teachplan::getId,teachPlanId);
        Teachplan teachplan = this.getOne(queryWrapper);
        TeachplanDTO dto = TeachPlanConvert.INSTANCE.entity2dto(teachplan);
        CourseBase courseBase = verifyCourseMsg(dto, companyId); 
    }

    /* 添加操作
     *  业务分析：
     *       1.判断关键数据
     *           pname: 课程计划名称
     *       2.判断业务数据
     *           课程基本信息
     *               判断课程基本信息是否是同一家机构
     *               判断课程是否删除
     *               判断课程审核状态
     *                   只有审核未通过或未提交才可以操作
     *       3.将dto数据转为po数据
     *       4.获得父级节点数据（新添加的数据是没有parentid）
     *           获得parentid
     *           获得父级节点的等级：给新添加的数据赋值grade
     *           获得父级节点的子节点的个数：给新添加的数据赋值orderby
     *
     *           让课程计划和课程关联
     *
     *       5.添加课程计划数据
     *       6.转为dto并返回
     * */
    private TeachplanDTO createTeachPlan(TeachplanDTO dto, Long companyId) {
        //  1.判断关键数据
        //  pname: 课程计划名称
        if (StringUtil.isBlank(dto.getPname())||
                ObjectUtils.isEmpty(companyId)){
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        // 2.判断业务数据
        // 判断是否有课程数据：courseId
        // 判断课程数据是否存在
        // 判断课程是否删除
        // 判断是否是同一个机构
        CourseBase courseBase = verifyCourseMsg(dto, companyId);
        //      判断课程审核状态
        //          只有审核未通过或未提交才可以操作
        String auditStatus = courseBase.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus)
        ) {
            ExceptionCast.cast(ContentErrorCode.E_120011);
        }
        //4.获得父级节点数据（新添加的数据是没有parentid）
        //   获得parentid
        //   获得父级节点的等级：给新添加的数据赋值grade
        //   获得父级节点的子节点的个数：给新添加的数据赋值orderby
        Teachplan parentNode = getParentNode(dto,courseBase);
        //   让课程计划和父级数据关联
        Teachplan teachplan = TeachPlanConvert.INSTANCE.dto2entity(dto);
        teachplan.setParentid(parentNode.getId());
        // 设置当前课程计划的级别
        teachplan.setGrade(parentNode.getGrade()+1);
        // 设置当前课程计划的排序orderBy
        //  当前的teachplan的orderby：父级拥有的子级数据的个数+1
        LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Teachplan::getParentid, parentNode.getId());

        int count = this.count(queryWrapper);

        teachplan.setOrderby(count+1);
        // 保存课程基本信息数据
        boolean result = this.save(teachplan);
        //         判断保存的结果
        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }
        // 将po数据转为dto并返回
        TeachplanDTO resultDTO = TeachPlanConvert.INSTANCE.entity2dto(teachplan);


        return resultDTO;
    }

    /*
    * 获得课程计划的父级节点
    *
    *   1.课程计划的一级（根级）

        - 数据来源：课程基础信息（CourseBase）
        - 创建时机：在第一次添加二级数据时自动生成
        - 注意事项：一级父级（根级）父级 ID 值为 0

        2.课程计划的二级和三级节点

        - 数据来源：教育机构人员在页面填写的数据
        - 创建时机：教育机构人员提交课程计划数据
        - 注意事项：添加时要校验父级菜单是否存在
          - 二级在添加时
            - 父级不存在（一级）：自动创建并获得获得父级Id值
            - 父级存在（一级）：获得父级Id值
          - 三级在添加时
            - 父级不存在（二级）：抛出异常
            - 父级存在（二级）：获得父级Id值
    *
    *   机构添加的课程计划是能是：二级或三级
    *   判断新增的数据是二级或三级：依据是当前Teachplan 是否有parentId值
    *
    * */
    private Teachplan getParentNode(TeachplanDTO dto, CourseBase courseBase) {
        // 1.判断新增的数据是二级或三级：依据是当前Teachplan 是否有parentId值
        Long parentid = dto.getParentid();

        if (ObjectUtils.isEmpty(parentid)) {
            // 新增的课程计划是二级数据，需要获得一级课程计划

            // 获得一级课程计划
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getParentid, TeachPlanEnum.FIRST_PARENTID_FLAG);
            queryWrapper.eq(Teachplan::getCourseId,dto.getCourseId() );

            Teachplan rootNode = this.getOne(queryWrapper);

            if (ObjectUtils.isEmpty(rootNode)) {
                // - 父级不存在（一级）：自动创建并获得获得父级Id值
                rootNode = new Teachplan();
                rootNode.setPname(courseBase.getName());
                rootNode.setParentid(new Long(TeachPlanEnum.FIRST_PARENTID_FLAG));
                rootNode.setGrade(TeachPlanEnum.FIRST_LEVEL);
                rootNode.setDescription(courseBase.getDescription());
                rootNode.setOrderby(TeachPlanEnum.FIRST_LEVEL);
                rootNode.setCourseId(courseBase.getId());

                boolean result = this.save(rootNode);
                if (!result) {
                    ExceptionCast.cast(ContentErrorCode.E_120415);
                }

            }
            // - 父级存在（一级）：获得父级Id值
            return rootNode;
        } else {
            // 新增的课程计划是三级数据，需要获得二级课程计划

            // 获得二级课程计划数据
            Teachplan secNode = this.getById(dto.getParentid());

            if (ObjectUtils.isEmpty(secNode)) {
                ExceptionCast.cast(ContentErrorCode.E_120408);
            }
            return secNode;
        }
    }

    private CourseBase verifyCourseMsg(TeachplanDTO dto, Long companyId) {
        if (ObjectUtils.isEmpty(dto.getCourseId())) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        // 查询课程基本信息是否存在（课程计划是没有独立存在的意义）
        CourseBase courseBase = courseBaseService.getById(dto.getCourseId());
        if (ObjectUtils.isEmpty(courseBase)) {
            ExceptionCast.cast(ContentErrorCode.E_120013);
        }
        //判断课程是否删除
        if (CommonEnum.DELETE_FLAG.getCodeInt().equals(courseBase.getStatus())) {
            ExceptionCast.cast(ContentErrorCode.E_120018);
        }
        // 判断课程是否是同一个教学机构
        if (!(ObjectUtils.nullSafeEquals(courseBase.getCompanyId(), companyId))) {
            ExceptionCast.cast(CommonErrorCode.E_100108);
        }
        return courseBase;
    }
    /*
       * 修改操作--课程计划
       *
       *    1.判断关键数据
       *    2.判断是否有课程数据：courseId
            3.判断课程数据是否存在
            4.判断课程审核状态
            5.判断课程是否删除
            6.判断是否是同一个机构
            7.修改课程计划数据
               有些数据是不让前端来修改
               *   parentid
               *   grade
               *   orderby
               *   courseid
             8.判断修改的结果
               依据数据：课程计划的id值 where id =xxx
               *
             9.返回结果数据 po-> dto
       * */
    private TeachplanDTO modifyTeachPlan(TeachplanDTO dto, Long companyId) {
        // 1.判断关键数据
        // pname:课程计划名称
        // companyId：机构id值
        if (StringUtil.isBlank(dto.getPname()) ||
                ObjectUtils.isEmpty(companyId)
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        // 2.判断是否有课程数据：courseId
        // 3.判断课程数据是否存在
        // 5.判断课程是否删除
        // 6.判断是否是同一个机构
        CourseBase courseBase = verifyCourseMsg(dto, companyId);
        // 4.判断课程审核状态
        String auditStatus = courseBase.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus)
        ) {
            ExceptionCast.cast(ContentErrorCode.E_120011);
        }
        // 7.修改课程计划数据

        // 判断课程计划是否存在
        Teachplan teachplan = this.getById(dto.getTeachPlanid());

        if (ObjectUtils.isEmpty(teachplan)) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }
        // 有些数据是不让前端来修改
        //   parentid
        dto.setParentid(teachplan.getParentid());
        //   grade
        dto.setGrade(teachplan.getGrade());
        //   orderby
        dto.setOrderby(teachplan.getOrderby());
        //   courseid
        dto.setCourseId(teachplan.getCourseId());
        // 8.判断修改的结果
        Teachplan po = TeachPlanConvert.INSTANCE.dto2entity(dto);

        boolean result = this.updateById(po);

        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }


        // 9.返回结果数据 po-> dto
        return dto;
    }


    private void generateTeachNodes(List<TeachplanNode> nodes, TeachplanNode rootNode) {
        // 判断父级的集合是否为空，为空则需要创建出集合对象
        if (CollectionUtils.isEmpty(rootNode.getChildrenNodes())){

            rootNode.setChildrenNodes(new ArrayList<>());
        }
        for (TeachplanNode node : nodes) {
            //1.1 将循环中的 node 添加到当前父级 node 集合数据中
            if (ObjectUtils.nullSafeEquals(rootNode.getId(),node.getParentid())){
                rootNode.getChildrenNodes().add(node);
                if (!(node.getGrade().equals(TeachPlanEnum.THIRD_LEVEL))){
                    generateTeachNodes(nodes,node);
                }
            }
        }
    }
}
